package cn.txun.csmall.passport.service.impl;

import cn.txun.csmall.commons.web.ServiceCode;
import cn.txun.csmall.commons.ex.ServiceException;
import cn.txun.csmall.passport.mapper.AdminMapper;
import cn.txun.csmall.passport.mapper.AdminRoleMapper;
import cn.txun.csmall.passport.pojo.entity.Admin;
import cn.txun.csmall.passport.pojo.entity.AdminRole;
import cn.txun.csmall.passport.pojo.param.AdminAddNewParam;
import cn.txun.csmall.passport.pojo.param.AdminLoginInfoParam;
import cn.txun.csmall.passport.security.AdminDetails;
import cn.txun.csmall.passport.service.IAdminService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


//@Transactional
@Service
@Slf4j
public class AdminServiceImpl implements IAdminService {
    @Value("${csmall.jwt.secret-key}")
    private String secretKey ;
    @Value("${csmall.jwt.duration-in-minute}")
    private Long durationInMinute;
    @Autowired
    private AdminMapper adminMapper;
    @Autowired
    private AdminRoleMapper adminRoleMapper;
    @Autowired
    private AuthenticationManager authenticationManager;

    //@Transactional
    @Override
    public void addNew(AdminAddNewParam adminAddNewParam) {
        log.debug("开始处理【添加管理员】的业务，参数：{}",adminAddNewParam);
        //检查管理员名称是否被占用，如果占用，则抛出异常
        QueryWrapper<Admin> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username",adminAddNewParam.getUsername());// name="参数中相册的名称"
        int countByName = adminMapper.selectCount(queryWrapper);
        log.debug("根据管理员名称统计匹配的管理员数据：结果：{}",countByName);
        if(countByName>0){
            String message="添加管理员失败，管理员名称已经被占用";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT,message);
        }
        //TODO  判断电话号码重复
        //TODO  判断邮箱重复
        //将管理员写入数据库中
        Admin admin = new Admin();
        BeanUtils.copyProperties(adminAddNewParam,admin);
        admin.setLastLoginIp(null);
        admin.setLoginCount(0);
        admin.setGmtLastLogin(null);
        admin.setGmtCreate(LocalDateTime.now());
        admin.setGmtModified(LocalDateTime.now());
        log.debug("准备将新的管理员数据存入数据库，数据：{}",admin);
        int rows = adminMapper.insert(admin);
        if(rows!=1){
            String message = "添加管理员失败，服务器忙，请稍后再试";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT,message);
        }
        log.debug("将新的管理员数据写入数据库完成!");

        //将管理员角色数据插入ams_admin_role
        Long[] roleIds = adminAddNewParam.getRoleIds();
        AdminRole[] adminRoleList = new AdminRole[roleIds.length];
        for(int i=0 ;  i<roleIds.length ; i++){
            AdminRole adminRole = new AdminRole();
            adminRole.setAdminId(admin.getId());
            adminRole.setRoleId(roleIds[i]);
            adminRole.setGmtCreate(LocalDateTime.now());
            adminRole.setGmtModified(LocalDateTime.now());
            adminRoleList[i] = adminRole;
        }
        rows =  adminRoleMapper.insertBatch(adminRoleList);
        if(rows!=roleIds.length){
            String message = "添加管理员失败，服务器忙，请稍后再试";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT,message);
        }
        log.debug("将新的管理员与角色的关联数据写入数据库完成!");
    }

    @Override
    public String login(AdminLoginInfoParam adminLoginInfoParam) {
        log.debug("开始处理【管理员登录】的业务，参数：{}",adminLoginInfoParam);
        //创建认证时所需要的参数对象
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                        adminLoginInfoParam.getUsername(),
                        adminLoginInfoParam.getPassword()
                );
        //执行认证，并获取认证结果
        Authentication authenticateResult
                = authenticationManager.authenticate(authentication);
        log.debug("登录认证完成，认证结果：{}",authenticateResult);
        //从认证结果中取出当事人信息
        Object principal = authenticateResult.getPrincipal();
        AdminDetails adminDetails = (AdminDetails)principal;
        log.debug("从认证结果取出当事人信息：{}",adminDetails);
        //生成JWT
        //取出当事人中权限
        Collection<GrantedAuthority> authorities = adminDetails.getAuthorities();
        Map<String,Object> claims = new HashMap<>();
        claims.put("id",adminDetails.getId());
        claims.put("username",adminDetails.getUsername());
        //将权限放在JWT载荷(数据)
        claims.put("authoritiesJSONString", JSON.toJSONString(authorities));

        String jwt = Jwts.builder()
                //    - Header（头部）：声明算法与Token类型
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                //     - Payload（载荷）：数据
                .setClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis()+durationInMinute*60*1000))
                //    - Verify Signature（验证签名）
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
        log.debug("生成了此管理员的信息对应的JWT:{}",jwt);

        return  jwt;
        //====使用JWT以后不需要以下2行代码===
        //将认证结果存入SecurityContext(上下文)中
        /*SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(authenticateResult);*/
    }
}
